from PyQt5.QtCore import Qt, pyqtSignal, QMimeData
from PyQt5.QtGui import QDrag, QPixmap
from PyQt5.QtWidgets import QApplication, QLabel, QWidget, QVBoxLayout, QHBoxLayout, QMainWindow, QPushButton

class DragTargetIndicator(QLabel):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setStyleSheet("QLabel { background-color: rgb(235, 212, 82); border: 1px solid black; }")

class ActionLabel(QLabel):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setAlignment(Qt.AlignLeft)
        self.data = self.text()
        self.setObjectName('Action')
        self.setMouseTracking(True)
        self.setStyleSheet("QLabel:hover { background-color: #6EB5FF; }")
        self.setToolTip('Double Click to Edit')
        self.setContextMenuPolicy(Qt.CustomContextMenu)

    def mouseMoveEvent(self, event):
        if event.buttons() == Qt.LeftButton:
            drag = QDrag(self)
            mime = QMimeData()
            drag.setMimeData(mime)
            pixmap = QPixmap(self.size())
            self.render(pixmap)
            drag.setPixmap(pixmap)
            drag.exec_(Qt.MoveAction)
            self.show()

class ActionsWidget(QWidget):
    orderChanged = pyqtSignal(list)

    def __init__(self, orientation=Qt.Vertical):
        super().__init__()
        self.setAcceptDrops(True)
        self.orientation = orientation
        self.layout = QVBoxLayout() if orientation == Qt.Vertical else QHBoxLayout()
        self.drag_target_indicator = DragTargetIndicator()
        self.layout.addWidget(self.drag_target_indicator)
        self.drag_target_indicator.hide()
        self.setLayout(self.layout)

    def dragEnterEvent(self, event):
        event.accept()

    def dragLeaveEvent(self, event):
        self.drag_target_indicator.hide()
        event.accept()

    def dragMoveEvent(self, event):
        moved_widget = event.source()
        if self.isAncestorOf(moved_widget):
            index = self._find_drop_location(event)
            if index is not None:
                self.layout.insertWidget(index, self.drag_target_indicator)
                event.source().hide()
                self.drag_target_indicator.show()
                event.accept()
            else:
                event.ignore()

    def dropEvent(self, event):
        moved_widget = event.source()
        self.drag_target_indicator.hide()
        index = self.layout.indexOf(self.drag_target_indicator)
        if index is not None:
            self.layout.insertWidget(index, moved_widget)
            self.orderChanged.emit(self.get_item_data())
            moved_widget.show()
            self.layout.activate()
        event.accept()

    def _find_drop_location(self, event):
        pos = event.pos()
        spacing = self.layout.spacing() / 2
        for n in range(self.layout.count()):
            w = self.layout.itemAt(n).widget()
            drop_here = (pos.y() >= w.y() - spacing and pos.y() <= w.y() + w.size().height() + spacing) if self.orientation == Qt.Vertical else (pos.x() >= w.x() - spacing and pos.x() <= w.x() + w.size().width() + spacing)
            if drop_here:
                return n
        return None

    def add_item(self, item):
        self.layout.addWidget(item)

    def get_item_data(self):
        data = []
        for n in range(self.layout.count()):
            w = self.layout.itemAt(n).widget()
            if hasattr(w, "data"):
                data.append(w.data)
        return data

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.add_btn = QPushButton('Add')
        step_1 = ActionsWidget(orientation=Qt.Vertical)
        for value in ["A", "B", "C", "D"]:
            item = ActionLabel(value)
            step_1.add_item(item)
        step_1.orderChanged.connect(print)

        step_2 = ActionsWidget(orientation=Qt.Vertical)
        for value in ["JJ", "KK", "LL"]:
            item = ActionLabel(value)
            step_2.add_item(item)
        step_2.orderChanged.connect(print)

        self.add_btn.clicked.connect(lambda event, step=step_1: self.add_action(event, step))

        container = QWidget()
        layout = QVBoxLayout()
        layout.addStretch(1)
        layout.addWidget(QLabel('Step 1'))
        layout.addWidget(QLabel('Step Description'))
        layout.addWidget(step_1)
        layout.addWidget(step_2)
        layout.addStretch(1)
        layout.addWidget(self.add_btn)
        container.setLayout(layout)

        self.setCentralWidget(container)

    def add_action(self, event, step):
        item = ActionLabel('E')
        step.add_item(item)

if __name__ == '__main__':
    app = QApplication([])
    w = MainWindow()
    w.show()
    app.exec_()